# 透视变换:将矩形映射为任意四边形
# cv2.getPerspectiveTransform() #获取变换矩阵
# cv2.warpPerspective() # 透视变换函数

import cv2
import numpy

# 顺时针排序四个点(左上角为第一个点)
def sort_corners(pts_in):
    if not isinstance(pts_in, numpy.ndarray):
        return None
    
    if pts_in.shape != (4, 1, 2):
        return None
    print(pts_in)
    
    pts = pts_in.astype(numpy.float32)
    print(pts)
    
    # 计算质心
    centroid = numpy.mean(pts, axis=0)
    print(centroid)
    print(centroid[0,0], centroid[0,1])
    
    # 按照到质心的角度进行排序
    angles = numpy.arctan2(pts[:,:,1] - centroid[0,1], pts[:,:,0] - centroid[0,0])
    print(angles)
    
    # 排序输出索引
    indexs = numpy.argsort(angles, 0)
    print(indexs)
    
    sorted_pts = numpy.zeros(shape=(4, 1, 2), dtype=numpy.float32)
    i = 0
    while i < len(pts):
        sorted_pts[i] = pts[indexs[i]]
        i = i + 1
    print(sorted_pts)
    
    return sorted_pts

image_bgr = cv2.imread("images/paper.png")
if image_bgr is None:
    exit(1)

image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
cv2.imshow("image_gray", image_gray)

threshold_ret, image_binary = cv2.threshold(image_gray, 128.0, 255.0, cv2.THRESH_BINARY)
cv2.imshow("image_binary", image_binary)

# 获取轮廓
contours, hierarchy = cv2.findContours(image_binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

for contour in contours:
    contour_area = cv2.contourArea(contour)
    if contour_area > 1000:
        # print(contour)
        
        # 计算轮廓周长，用于后续逼近精度
        perimeter = cv2.arcLength(contour, True)
        
        # 多边形逼近，epsilon 是精度系数（越小越接近原轮廓）
        approx = cv2.approxPolyDP(contour, epsilon=0.02 * perimeter, closed=True)
        
        # 如果逼近后的轮廓有4个顶点，则可能是我们要找的梯形
        if len(approx) == 4:
            print(approx)
            cv2.drawContours(image_bgr, [approx], -1, (0, 0, 255), 2)
            
            # 按角度排序矩形四个角的坐标
            sorted_pts = sort_corners(approx)
            
            # 原始矩形四个角的坐标
            src = sorted_pts.reshape(4, 2)
            
            # 变换后矩形四个角的坐标
            dest = numpy.array([[0.0,0.0], [400.0,0.0], [400.0,400.0], [0.0,400.0]], dtype=numpy.float32)
            
            # 获取透视变换矩阵
            fun_mat = cv2.getPerspectiveTransform(src, dest)
            if fun_mat is None:
                print("getPerspectiveTransform error\n")
                exit(1)
            
            # 执行透视变换
            image_out = cv2.warpPerspective(image_bgr, fun_mat, (400, 400))

cv2.imshow("image_bgr", image_bgr)
cv2.imshow("image_out", image_out)
cv2.waitKey()
cv2.destroyAllWindows()